#include <libssh2.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/select.h>
#include <arpa/inet.h>
#include <sys/time.h>
#include <sys/types.h>
#include <fcntl.h>
#include <errno.h>
#include <ctype.h>
#include "gaia_common.h"

static int waitsocket(int socket_fd, LIBSSH2_SESSION *session)
{
    struct timeval timeout;
    int rc;
    fd_set fd;
    fd_set *writefd = NULL;
    fd_set *readfd = NULL;
    int dir;
    /* 设置超时时间 10s */
    timeout.tv_sec = 10;
    timeout.tv_usec = 0;

    FD_ZERO(&fd);

    FD_SET(socket_fd, &fd);

    /* now make sure we wait in the correct direction */
    dir = libssh2_session_block_directions(session);

    if(dir & LIBSSH2_SESSION_BLOCK_INBOUND)
        readfd = &fd;

    if(dir & LIBSSH2_SESSION_BLOCK_OUTBOUND)
        writefd = &fd;

    rc = select(socket_fd + 1, readfd, writefd, NULL, &timeout);

    return rc;
}


/* 验证是否能通过ssh连上远端主机 */
int gaia_check_auth_via_password(const char *username,const char *password,LIBSSH2_SESSION *session)
{
  printf("username = %s,password = %s\n",username,password);
  int rc = 0;
  if ( strlen(password) != 0 ) {
    /* 通过密码进行验证 */
    while ((rc = libssh2_userauth_password(session, username, password)) ==
           LIBSSH2_ERROR_EAGAIN);
    if (rc) {
      fprintf(stderr, "Authentication by password failed.\n");
      //goto shutdown;
      return -1;
    }
  }
  
  if (rc) {
      fprintf(stderr, "\tAuthentication by public key failed\n");
      //goto shutdown;
      return -1;
   }
  
    return 0;
}

/***************************************************
 * 函数名：gaia_remote_operation
 * 函数功能：ssh远程执行操作 
 * 函数返回值：成功　０,失败　1
 * 函数 host_status用于存放执行命令结果 
 ****************************************************/
int gaia_remote_operation(const char *hostname,const char *username,const char *password,uint16_t port,const char *commandline,char *host_status)
{
  /* hostname,username,password 均不能为空 */
  if( (hostname == NULL) || (username == NULL) || (password == NULL) )
  {
    fprintf(stderr,"argument fail on the  gaia_remote_operation!\n");
    return -1;
  }


printf("commandline = %s\n",commandline);

  /* 套接字 */
  int sock;
  /* 接收函数返回值 */
  int rc;
  /* 异常退出的错误码 */
  int exitcode;
  /* */
  char *exitsignal = (char *)"none";
  /*********************************
   * 接收到libssh2执行命令返回的字节数 
   * 通常是执行命令，标准输出的结果
   ********************************/
  int bytecount = 0;

  size_t len;
  int type;

  /********************************* 
   * 套接字结构体 
   * 建立连接时，需要对其成员进行初始化
   *********************************/
  struct sockaddr_in sin;

  /* libssh2里的session和channel*/
  LIBSSH2_SESSION *session;
  LIBSSH2_CHANNEL *channel;

  /* 执行的命令不能为空 */
  if(commandline == NULL)
  {
    fprintf(stderr,"Error,command is not null!\n");
    return -1;
  }
  
  /* libssh2初始化 */
  rc = libssh2_init(0);
  if(rc != 0)
  {
    fprintf(stderr,"libssh2 initialization failed (%d)!\n",rc);
    return -1;
  }

  printf("test testste\n");

  /* 初始化sock连接,在TCP/IP三次握手建立连接阶段 */
  if( (rc = gaia_init_ssh2_socket(&sock,hostname,port,&sin)) != 0 )
  {
    fprintf(stderr,"connect host %s fail!\n",hostname);
    return -1;
  }

  /* 建立一个ssh2 session */
  session = libssh2_session_init();
  if (!session)
  {
    fprintf(stderr,"init ssh2 session fail!\n");
    return -1;
  }
 
  /* 0为非阻塞模式 */
  libssh2_session_set_blocking(session,0);
  /* 还不清楚用途，按照官方示例添加的 */
  while( (rc = libssh2_session_handshake(session,sock)) == LIBSSH2_ERROR_EAGAIN );
  /* 测试ssh session是否创建成功 */
  if(rc)
  {
    fprintf(stderr,"Fail establishing SSH session: %d\n",rc);
    goto ERROR;
    //return -1;
  }

  /* 测试ssh账号是否通过验证 */
  if( (rc = gaia_check_auth_via_password(username,password,session)) != 0)
  {
    fprintf(stderr,"Authencation by password fail!\n");
    close(sock);
    goto ERROR;
    //return -1;
  }
  
  while( (channel = libssh2_channel_open_session(session)) == NULL &&
         libssh2_session_last_error(session,NULL,NULL,0) == LIBSSH2_ERROR_EAGAIN )
  {
    waitsocket(sock,session);
  }

  if(channel == NULL)
  {
    fprintf(stderr,"Open channel session error!\n");
    close(sock);
    goto ERROR;
    //return -1;
  }

  /* ssh远程执行命令 */
  while( (rc = libssh2_channel_exec(channel,commandline)) == LIBSSH2_ERROR_EAGAIN )  
    waitsocket(sock,session);

  if(rc != 0)
  {
    fprintf(stderr,"libssh2 channel exec fail!\n");
    close(sock);
    goto ERROR;
    //return -1;
  }
  /* 打印执行命令的结果 */
  char buffer[0x4000];
  for( ;; )
  {
    /* loop until we block */
    int rc;
    do
    {
      rc = libssh2_channel_read( channel, buffer, sizeof(buffer) );
      if( rc > 0 )
      {
        int i;
        bytecount += rc;
		/* 打印执行命令的结果.如：
		 * 执行uptime,则将uptime的输出打印	 
		 */
          //fprintf(stderr, "We read:\n");
          //for( i=0; i < rc; ++i )
           //fputc( buffer[i], stderr);
          //fprintf(stderr, "\n");
      }
      else {
        if( rc != LIBSSH2_ERROR_EAGAIN )
          /* no need to output this for the EAGAIN case */
          fprintf(stderr, "libssh2_channel_read returned %d\n", rc);
      }
          //fprintf(stderr,"\n");
    }
    while( rc > 0 );

    /* this is due to blocking that would occur otherwise so we loop on
       this condition */
    if( rc == LIBSSH2_ERROR_EAGAIN )
    {
      waitsocket(sock, session);
    }
    else
      break;
  }
  //memcpy(host_status,buffer,sizeof(buffer));
  strncpy(host_status,buffer,strlen(buffer));
  //printf("buffer = %s\n",buffer);
  printf("execute result:\t%s\n",host_status);

  /* 指定异常退出码 */
  exitcode = 127;
  
  while( (rc = libssh2_channel_close(channel)) == LIBSSH2_ERROR_EAGAIN )
    waitsocket(sock,session);

  if(rc == 0)
  {
    exitcode = libssh2_channel_get_exit_status(channel);
    libssh2_channel_get_exit_signal(channel,&exitsignal,
                                    NULL,NULL,NULL,NULL,NULL);
  }

  if(exitsignal)
    fprintf(stderr,"\nGot signal: %s\n",exitsignal);
  else
    fprintf(stderr,"\nEXIT: %d bytecount: %d\n",exitcode,bytecount);

  libssh2_channel_free(channel);
  channel = NULL;
  
  return 0;

 ERROR:
  libssh2_session_disconnect(session,"Error Occurrence,Session shutdown");
  libssh2_session_free(session);
  return -1;
}

/* just for test */
/*
int main(int argc, char *argv[])
{
  const char *hostname = "192.168.10.246";
  const char *commandline = argv[1];
  const char *username = "root";
  const char *password = "shopex";
  uint16_t port = 22;
  int rc;
  char status[128];
  
  rc = gaia_remote_operation(hostname,username,password,port,commandline,status);
  if(rc != 0)
  {
    fprintf(stderr,"remote operation fail!\n");
    exit(1);
  }

  printf("status = %s\n",status);
  
  return 0;
}
*/
